Not only is R the very best language for statistical inference, it is rapidly evolving to accommodate the needs of data scientists. In this session, we will discuss some of the tools available in R for practicing data science. We will begin with a review some of the fundamental strengths of the R language and then discuss how R is integrating into the world of production level data science.

For the fourth year in a row, R has made it into the top 10 list of the IEEE Spectrum Magazine’s survey of general purpose programming languages. That R should be there at all is astounding! Much of R’s popularity can be attributed to the growth of data science.

A recent StackOverflow Post contained the following chart which shows the top twelve industries in the United States and the United Kingdom that generated R related questions.

The chart clearly shows that after Academia, R traffic originating from the Healthcare dominates the traffic by generated by other industries.

Moreover, another chart published in the same post shows R traffic is generally growing faster in industries where it was already popular. Academia and Healthcare are pulling away from the pack.



In a follow-up Stackoverflow post R was identified as the least disliked language.


Our Agenda

After a short discussion of why the R Language itself is particularly well suited to data science, we will look at examples of R tools in seven categories:
* Working in R
* Manipulating Data
* Accessing Data
* Visualizing Data
* Predictive Modeling
* Machine Learning with Big Data
* Sharing Results

Agenda Details
* The R Language itself (10 min)
* A very brief history
* The Structure of R
* The R Package System
* Tools for Working in R (5 min)
* R Markdown
* R Notebooks
* Tools for Manipulating Data (20 min)
* The Tidyverse
* Tools for Accessing Data (5 min)
* Accessing Databases
* Tools for Visualizing Data (5 min)
* htmlwidgets
* Trelliscope
* Predictive Modeling Tools (10 min)
* The caret package
* Tools for Big Data Platforms (30 min)
* Spark and Sparklyr
* Keras and Tensor Flow
* Tools for Sharing Results (5 min)
* Shiny

________________________________________________________________________________________

The R Language Itself

A Very Brief History of R

To appreciate this R’s growth, consider that R evolved from a relatively modest effort to develop an interface to Fortran statistical routines to a full featured language that provides access to the world’s richest repository of statistical and machine learning algorithms. The following dates outline a short history of how this happened.


R is a direct descendant of the S Language developed at Bell Labs by John Chambers, Rick Becker and other, but it was also heavily influenced by Lisp and Scheme. Robert Gentleman and Ross Ihaka introduced Lisp like semantics to an ideas such as lexical scoping.

________________________________________________________________________________________

The Structure of the R Language

R is all about “Flow”

The R language was designed to enhance the experience of data exploration and statistical inference. The original intent of the S designers to make a convenient interface for their high powered Fortran rourines evolved into a project to make R a full-featured language for statistical inference and modeling. People productivity is at the core of the R experience. As John Chambers puts it in his book Extending R, “One of the attractions of R has always been the ability to compute an interesting result quickly.” Ease of use and the ability to stay in the “flow” of an analysis is more important than computational efficiency.

R is a functional, object-based language. The three basic principles underlying R are:
* Everything that exists in R is an object
* Everything that happens in R is a function call
* Interfaces to other software are part of R

Chambers elaborates on this last point as follows: “A key motivation for the original S remains important now: to give easy access to the best computations for understanding data.” We see several examples of the efforts R developers put into connecting to the best things out there later in this presentation.

Objects and Functions

The interplay of objects and functions in R is apparent in the way in which data and statistical models are packaged. For example consider a simple linear model:

x <- rnorm(100)
y <- rnorm(100)
reg <- lm(y ~ x)
summary(reg)

Call:
lm(formula = y ~ x)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.7082 -0.7468  0.2222  0.7854  2.2538 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.08342    0.11116  -0.750    0.455
x            0.13427    0.11811   1.137    0.258

Residual standard error: 1.111 on 98 degrees of freedom
Multiple R-squared:  0.01301,   Adjusted R-squared:  0.002943 
F-statistic: 1.292 on 1 and 98 DF,  p-value: 0.2584

As the summary above indicates, R only returns model outputs when you ask for them and methods for generating summaries tend to be parsimonious. The model object reg packages quite a bit of information about the model.

str(reg,give.attr=FALSE)
List of 12
 $ coefficients : Named num [1:2] -0.0834 0.1343
 $ residuals    : Named num [1:100] 1.461 0.334 -1.726 0.842 0.874 ...
 $ effects      : Named num [1:100] 0.88 -1.263 -1.854 0.723 0.718 ...
 $ rank         : int 2
 $ fitted.values: Named num [1:100] -0.0796 0.0257 -0.1211 -0.186 0.0674 ...
 $ assign       : int [1:2] 0 1
 $ qr           :List of 5
  ..$ qr   : num [1:100, 1:2] -10 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ...
  ..$ qraux: num [1:2] 1.1 1.09
  ..$ pivot: int [1:2] 1 2
  ..$ tol  : num 1e-07
  ..$ rank : int 2
 $ df.residual  : int 98
 $ xlevels      : Named list()
 $ call         : language lm(formula = y ~ x)
 $ terms        :Classes 'terms', 'formula'  language y ~ x
 $ model        :'data.frame':  100 obs. of  2 variables:
  ..$ y: num [1:100] 1.381 0.36 -1.847 0.656 0.941 ...
  ..$ x: num [1:100] 0.0282 0.8127 -0.2805 -0.7643 1.1236 ...

Functions in R can call other functions. This feature is well adapted to writing functions to estimate maximum likelihood and other statistical algorithms.

# Two different rounding options
round(pi,4); signif(pi,4)     
[1] 3.1416
[1] 3.142
# This is the way to have one function call another function
jmean <- function(x,FUN,...){
               m <- FUN(sum(x)/length(x),...)
               return(m)}
x <- rnorm(100)
jmean(x,round,4); jmean(x, signif,4)
[1] -0.0278
[1] -0.02777

Missing Values

A significant advantage that R has over other scripting languages is that it has an innate mechanism for dealing with missing values: NA.

z <- c(1:3, NA)
z
[1]  1  2  3 NA
is.na(z)
[1] FALSE FALSE FALSE  TRUE

It is easy to identify and work with missing values in R.

a <- 1:5
b <- rnorm(5)
c <- LETTERS[1:5]
dF <- data.frame(a,b,c)
dF$b[3] <- NA; dF$c[4] <- NA
dF
na.omit(dF)

R also has several packages devoted to missing value imputation including: Amelia, BaBooN, cat, ForImp, Hmisc, impute, imputeMDR, kmi, mi, mice, MImix, missForest, MissingDataGui, missMDA, mitools, mix, mtsdi, norm, pan, robCompositions, rrcovNA, sbgcop, VIM, yaImpute, Zelig

The Data Frame

A data frame is a list in which each row may represent an observation and each column a variable, is a natural data structure for statistical analysis. This consistent, ubiquitous data structure provides R with a big technical advantage. This is no accident. Both Robert Gentleman and Ross Ihaka believed in Niklaus Wirth’s dictum: algorithms + data structures = programs.

________________________________________________________________________________________

The R Package System

There are over 11,700 packages on CRAN, R’s central repository. The algorithms in these packages comprise a transparent, documented statistical resource of great value. Searching the through CRAN is a challenge, but the CRAN Task Views lists of R packages, curated by experts and organized by application area mitigate the problem.

________________________________________________________________________________________

Tools for Working in R

A good place to start is with an IDE and R Markdown Let’s take a few minutes to examine this notebook and the R Markdown code.

________________________________________________________________________________________

Tools for Manipulating Data

The Tidyverse

Another great advantage of R is its ability to facilitate the construction of Domain Specific Languages. As Shiny’s creator Joe Cheng puts it (Interiew with Joe Cheng):

If you look beyond the syntax, R really is conceptually very much like Lisp in a lot of ways. One of those ways is that it makes it very, very easy to compute on the programming language itself. …

I think day-to-day, R programmers probably don’t think about these things, but the elegant, terse syntax of dplyr and the pipe operator are possible because of how malleable a language R is and how great it is for writing DSLs in it.

Personally, one of my pet peeves during these language wars is when people say that one of the dierences between say Python or Julia and R is that R is a DSL for stats, whereas these other things are general purpose languages. R is not a DSL. It’s a language for writing DSLs, which is something that’s altogether more powerful. I actually think that Julia has many of these same characteristics, but Python, even though it obviously has its own strengths, certainly doesn’t share that same level of fexibility.

One way to think of the Tidyverse is that it is a DSL for doing data science. The packages that comprise the tidyverse offer a consistent, integrated set of functions for implementing the canonical data science workflow.



The name “tidyverse” comes from the concept of “tidy” data which Hadley Wickham, the tidyvrese’s principal architect and implementer, describes as follows:

Tidy data is a standard way of mapping the meaning of a dataset to its structure. A dataset is messy or tidy depending on how rows, columns and tables are matched up with observations, variables and types. In tidy data: (1) Each variable forms a column, (2) Each observation forms a row and (3) Each type of observational unit forms a table.

Manipulating and Transforming Data

The dplyr package which provides a consistent set of verbs for manipulating data provides the backbone structure for the import, tydy and transform sequence of the data science workflow. The basic dplyr verbs are: * filter() to select cases based on their values
* arrange() to reorder the cases
* select() and rename() to select variables based on their names
* mutate() and transmute() to add new variables that are functions of existing variables
* summarise() to condense multiple values to a single value
* sample_n() and sample_frac() to take random samples

Here, we will look at a few examples of working with these verbs using nycflights13::flights, a dataset containing all 336776 flights that departed from New York City in 2013. The data comes from the US Bureau of Transportation Statistics, and is documented in ?nycflights13

#install.packages("tidyverse","nycflights13")
library(nycflights13)
library(tidyverse)
data(flights)
flights

In this first example, we use dplyr::filter() to fetch flights that arrived early in January.

# filter by departure delay and print the first few records
flights %>% filter(month == 1, dep_delay < 0)

arrange() reorders rows. It takes a data frame, and a set of column names (or more complicated expressions) to order by. If you provide more than one column name, each additional column will be used to break ties in the values of preceding columns:

arrange(flights, sched_dep_time, dep_delay)

select() allows you to select columns by name. Here we select the columns origin, dest and air_time, and then sort by air_time from largest to smallest.

select(flights, origin, dest, air_time) %>% arrange(desc(air_time))

mutate() allows you to add a new column to a data frame. Here we add in a column for airspeed.

mutate(flights,
  gain = arr_delay - dep_delay,
  speed = distance / air_time * 60
)

summarise() will compute a statistic and collapse a data frame into a single row.

dplyr::summarise(flights,
  delay = mean(dep_delay, na.rm = TRUE)
)

The group_by() functions allows you use the verbs discussed above on groups of observations in a dataset. Here we compute mean distance and arrival delay for each individual plane and plot the results.

by_tailnum <- group_by(flights, tailnum)
delay <- dplyr::summarise(by_tailnum,
  count = n(),
  dist = mean(distance, na.rm = TRUE),
  delay = mean(arr_delay, na.rm = TRUE))
delay <- filter(delay, count > 20, dist < 2000)
# plot delays
library(ggplot2)
ggplot(delay, aes(dist, delay)) +
  geom_point(aes(size = count), alpha = 1/2) +
  geom_smooth()  +
  scale_size_area(max_size = 4)

For a more complete presentation using this data see the dplyr vignette.

________________________________________________________________________________________

Tools for Accessing Data

Accessing Databases

With the aid of the DBI package, dply is able to import data from several different open source data bases including MySQL and MariaDB with the RMariaDB package, Postgres and Redshift with RPostgressSQL, and SQLite with RSQLite.

The following simple example illustrates working with SQLite. Again, we use the nycflights13 data set to create a SQLite table with 1,000 rows and 19 columns.

library(tidyverse)
library(DBI)
library(RSQLite)
library(dbplyr)

Attaching package: ‘dbplyr’

The following objects are masked from ‘package:dplyr’:

    ident, sql
library(nycflights13)
con <- DBI::dbConnect(RSQLite::SQLite(), path = ":memory:")
copy_to(con, nycflights13::flights, "flights",
  temporary = FALSE, 
  indexes = list(
    c("year", "month", "day"), 
    "carrier", 
    "tailnum",
    "dest"
  )
)
flights_db <- tbl(con, "flights")
flights_db 

Now that we have established a connection to the database table, we can use the normal dplyr verbs to query the data.

# List departure delay and arrival delay for year month and day.
flights_db %>% select(year:day, dep_delay, arr_delay)
# Find records with departure delay greater than 240 minutes.
flights_db %>% filter(dep_delay > 240)
# Summarize mean departure delay by destination airport
flights_db %>% 
  group_by(dest) %>%
  dplyr::summarise(delay = mean(dep_delay))
# disconnect from database
dbDisconnect(con)

dplyr can also be use with data stored in commercial databases. The bigquery enables working with data in Google’s BigQuery platform, and the odbc permits many commercial databases to be used as a dplyr backend through the open database connectivity protocol,

For additional information on basic dbplyr functionality see the vignette. For an advanced treatment using dplyr with commercial grade databases see the three blog posts from Edgar Ruiz:
* Databases using R
* Visualizations with R and Databases * Database Queries with R * Enterprise-ready dashboards with Shiny and databases.

Be sure to catch Edgar’s talk “Databases Using R” 11:30 AM tomorrow in room TR2

________________________________________________________________________________________

Tools for Visualizing Data

Javascript Visualizations with htmlwidgets

There are three major plotting systems in R: (1) base graphics, (2) lattice graphics and (3) ggplot2, an example of which we have seen above. Additionally, there is a significant amount of development work going on to allow R users to produce JavaScript visualizations directly from R. In this section we will see an example of working with R’s htmlwidgets library.

For this example we will work with the htmlwidget dygraphs which is useful for producing interactive time series visualizations and use it to visualize the monthly flow from the Nile River from 1871 through 1984.

library(pracma)             # for Nile river data
data(nile)
head(nile)

This data set is not tidy. For each year, the information about monthly flow is spread over several columns. To tidy things up, we need to reshape the data frame into “long form” where the columns comprise the variables Year, Month and Flow. In the code below, the tidy function gather() accomplishes this. arrange() sorts the data by Year and Month, and mutate adds a new date variable to the data frame.

nile_long <- nile %>% 
                  gather(Jan:Dec, key = "Month", value = "Flow") %>% 
                  arrange(Year,match(Month,month.abb)) %>% 
                  dplyr::mutate(
                  Date = as.Date(paste(Month,"-","15","-",as.character(Year),sep=""), format="%b-%d-%Y"))
  
head(nile_long)

Next, we produce some exploratory plots with ggplot2

# Plot the time series
p <- ggplot(nile_long[100:300,],aes(x=Date,y=Flow))
p + geom_line() + geom_point(shape=1,col="red") + 
  ylab("Flow in cubic meters / second") + 
  ggtitle("Monthly Flow of Nile River at Dongola Station")

# Boxplots of monthly flows
b <- ggplot(nile_long,aes(factor(Month),Flow))
b + geom_boxplot() +
  xlab("Month") +
  ylab("Flow in cubic meters / second") + 
  ggtitle("Variation of Flow at Dongola Station by Month")

We finish up by creating an interactive, JavaScript D3 interactive plot. To get the data ready for the dygraph() function we must make it an xts time series object.

# Create an interactive graph with a Javascript library
library(xts)
library(dygraphs)
# Make into a time series object
nile_ts <- xts(nile_long$Flow,
               order.by=nile_long$Date,
               frequency=12,start=c(1871,1))
# Plot wit htmlwidget dygraph
dygraph(nile_ts,ylab="cubic m / s", 
        main="Nile Monthly Flow Data") %>%
  dySeries("V1",label="Flow") %>%
  dyRangeSelector(dateWindow = c("1871-01-01","1984-12-01"))

To see some spectacular visualizations have a look at the htmlwidgets for R gallery.

________________________________________________________________________________________

Visualizing Large Data Sets with Trelliscope

Trelliscope is a system for producing detailed visualizations of very large, complex datasets. Thousands of plots are produced and the system computes metrics on each plot that describe interesting quantitative and qualitative features. Users can then sample, filter, or sort the plots based on the metrics. In Trelliscope terminology, these metrics are called “cognostics”, a term coined by John Tukey which stands for “computer guiding diagnostics”

Example using gapminder life expectancy data


Trelliscope is part of the DeltaRho Project. Look here for a paper that summarizes the system.

________________________________________________________________________________________

Predictive Modeling Tools

See the separate caret notebook.

________________________________________________________________________________________

Tools for Big Data Platforms

See the notebooks: * R_Keras_TensorFlow * sparlkyr

________________________________________________________________________________________

Tools for Sharing Results

Shiny

Shiny is an open source R package that provides an elegant and powerful web framework for building web applications using R. Shiny helps you turn your analyses into interactive web applications without requiring HTML, CSS, or JavaScript knowledge. Shiny has become an essential communication platform for several enterprise data science companies.

Some Enterprise Level Shiny Applications:
* LRT Signal Analysis for a Drug from the FDA


Some “How to” Shiny posts: * Enterprise-ready dashboards with Shiny and databases * Portfolio Volatility Shiny App * Printing From Flex Dashboard

To get started with Shiny: * The Shiny Showcase * Learn Shiny * Shiny Gallery

LS0tCnRpdGxlOiAiUiBUb29scyBmb3IgRGF0YSBTY2llbmNlIgphdXRob3I6IEpvc2VwaCBSaWNrZXJ0CmRhdGU6IDExLzAzLzE3Cm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KIVtdKG9kc2MucG5nKQoKPGJyLz4KPGJyLz4KCk5vdCBvbmx5IGlzIFIgdGhlIHZlcnkgYmVzdCBsYW5ndWFnZSBmb3Igc3RhdGlzdGljYWwgaW5mZXJlbmNlLCBpdCBpcyByYXBpZGx5IGV2b2x2aW5nIHRvIGFjY29tbW9kYXRlIHRoZSBuZWVkcyBvZiBkYXRhIHNjaWVudGlzdHMuIEluIHRoaXMgc2Vzc2lvbiwgd2Ugd2lsbCBkaXNjdXNzIHNvbWUgb2YgdGhlIHRvb2xzIGF2YWlsYWJsZSBpbiBSIGZvciBwcmFjdGljaW5nIGRhdGEgc2NpZW5jZS4gV2Ugd2lsbCBiZWdpbiB3aXRoIGEgcmV2aWV3IHNvbWUgb2YgdGhlIGZ1bmRhbWVudGFsIHN0cmVuZ3RocyBvZiB0aGUgUiBsYW5ndWFnZSBhbmQgdGhlbiBkaXNjdXNzIGhvdyBSIGlzIGludGVncmF0aW5nIGludG8gdGhlIHdvcmxkIG9mIHByb2R1Y3Rpb24gbGV2ZWwgZGF0YSBzY2llbmNlLgoKRm9yIHRoZSBmb3VydGggeWVhciBpbiBhIHJvdywgUiBoYXMgbWFkZSBpdCBpbnRvIHRoZSBbdG9wIDEwIGxpc3RdKGh0dHBzOi8vc3BlY3RydW0uaWVlZS5vcmcvY29tcHV0aW5nL3NvZnR3YXJlL3RoZS0yMDE3LXRvcC1wcm9ncmFtbWluZy1sYW5ndWFnZXMpIG9mIHRoZSBJRUVFIFNwZWN0cnVtIE1hZ2F6aW5lJ3Mgc3VydmV5IG9mIGdlbmVyYWwgcHVycG9zZSBwcm9ncmFtbWluZyBsYW5ndWFnZXMuIFRoYXQgUiBzaG91bGQgYmUgdGhlcmUgYXQgYWxsIGlzIGFzdG91bmRpbmchIE11Y2ggb2YgUidzIHBvcHVsYXJpdHkgY2FuIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIGdyb3d0aCBvZiBkYXRhIHNjaWVuY2UuIAo8YnIvPiAgIAohW10oSUVFRXJhbmsucG5nKQoKQSByZWNlbnQgW1N0YWNrT3ZlcmZsb3cgUG9zdF0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmJsb2cvMjAxNy8xMC8xMC9pbXByZXNzaXZlLWdyb3d0aC1yLykgY29udGFpbmVkIHRoZSBmb2xsb3dpbmcgY2hhcnQgd2hpY2ggc2hvd3MgdGhlIHRvcCB0d2VsdmUgaW5kdXN0cmllcyBpbiB0aGUgVW5pdGVkIFN0YXRlcyBhbmQgdGhlIFVuaXRlZCBLaW5nZG9tIHRoYXQgZ2VuZXJhdGVkIFIgcmVsYXRlZCBxdWVzdGlvbnMuIAoKIVtdKHZpc2l0c0J5SW5kdXN0cnkucG5nKQoKVGhlIGNoYXJ0IGNsZWFybHkgc2hvd3MgdGhhdCBhZnRlciBBY2FkZW1pYSwgUiB0cmFmZmljIG9yaWdpbmF0aW5nIGZyb20gdGhlIEhlYWx0aGNhcmUgZG9taW5hdGVzIHRoZSB0cmFmZmljIGJ5IGdlbmVyYXRlZCBieSBvdGhlciBpbmR1c3RyaWVzLgoKTW9yZW92ZXIsIGFub3RoZXIgY2hhcnQgcHVibGlzaGVkIGluIHRoZSBzYW1lIHBvc3Qgc2hvd3MgUiB0cmFmZmljIGlzIGdlbmVyYWxseSBncm93aW5nIGZhc3RlciBpbiBpbmR1c3RyaWVzIHdoZXJlIGl0IHdhcyBhbHJlYWR5IHBvcHVsYXIuIEFjYWRlbWlhIGFuZCBIZWFsdGhjYXJlIGFyZSBwdWxsaW5nIGF3YXkgZnJvbSB0aGUgcGFjay4KCiFbXShncm93dGhCeUluZHVzdHJ5LnBuZykKCjxici8+ICAgICAgCkluIGEgZm9sbG93LXVwIFN0YWNrb3ZlcmZsb3cgcG9zdCBSIHdhcyBpZGVudGlmaWVkIGFzIHRoZSBsZWFzdCBkaXNsaWtlZCBsYW5ndWFnZS4KCiFbXShkaXNsaWtlLnBuZykKCjxici8+ICAgICAKCiMjIE91ciBBZ2VuZGEgCkFmdGVyIGEgc2hvcnQgZGlzY3Vzc2lvbiBvZiB3aHkgdGhlIFIgTGFuZ3VhZ2UgaXRzZWxmIGlzIHBhcnRpY3VsYXJseSB3ZWxsIHN1aXRlZCB0byBkYXRhIHNjaWVuY2UsIHdlIHdpbGwgbG9vayBhdCBleGFtcGxlcyBvZiBSIHRvb2xzIGluIHNldmVuIGNhdGVnb3JpZXM6ICAgICAKKiBXb3JraW5nIGluIFIgICAKKiBNYW5pcHVsYXRpbmcgRGF0YSAgIAoqIEFjY2Vzc2luZyBEYXRhICAgCiogVmlzdWFsaXppbmcgRGF0YSAgIAoqIFByZWRpY3RpdmUgTW9kZWxpbmcgICAKKiBNYWNoaW5lIExlYXJuaW5nIHdpdGggQmlnIERhdGEgICAKKiBTaGFyaW5nIFJlc3VsdHMgICAKCioqQWdlbmRhIERldGFpbHMqKiAgICAKKiAqKlRoZSBSIExhbmd1YWdlIGl0c2VsZiAoMTAgbWluKSoqICAgCiogQSB2ZXJ5IGJyaWVmIGhpc3RvcnkgICAgCiogVGhlIFN0cnVjdHVyZSBvZiBSICAgIAoqIFRoZSBSIFBhY2thZ2UgU3lzdGVtICAgCiogKipUb29scyBmb3IgV29ya2luZyBpbiBSICg1IG1pbikqKiAgIAoqIFIgTWFya2Rvd24gICAgIAoqIFIgTm90ZWJvb2tzICAgCiogKipUb29scyBmb3IgTWFuaXB1bGF0aW5nIERhdGEgKDIwIG1pbikqKiAgIAoqIFRoZSBUaWR5dmVyc2UgICAKKiAqKlRvb2xzIGZvciBBY2Nlc3NpbmcgRGF0YSAoNSBtaW4pKiogIAoqIEFjY2Vzc2luZyBEYXRhYmFzZXMgICAgCiogKipUb29scyBmb3IgVmlzdWFsaXppbmcgRGF0YSAoNSBtaW4pKiogICAgCiogaHRtbHdpZGdldHMgIAoqIFRyZWxsaXNjb3BlICAgIAoqICoqUHJlZGljdGl2ZSBNb2RlbGluZyBUb29scyAoMTAgbWluKSoqICAgICAgCiogVGhlIGNhcmV0IHBhY2thZ2UgICAKKiAqKlRvb2xzIGZvciBCaWcgRGF0YSBQbGF0Zm9ybXMgKDMwIG1pbikqKiAgICAKKiBTcGFyayBhbmQgU3BhcmtseXIgICAKKiBLZXJhcyBhbmQgVGVuc29yIEZsb3cgICAKKiAqKlRvb2xzIGZvciBTaGFyaW5nIFJlc3VsdHMgKDUgbWluKSoqICAKKiBTaGlueSAgIAo8YnIvPiAgICAKCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIFRoZSBSIExhbmd1YWdlIEl0c2VsZgojIyMgQSBWZXJ5IEJyaWVmIEhpc3Rvcnkgb2YgUgoKVG8gYXBwcmVjaWF0ZSB0aGlzIFIncyBncm93dGgsIGNvbnNpZGVyIHRoYXQgUiBldm9sdmVkIGZyb20gYSByZWxhdGl2ZWx5IG1vZGVzdCBlZmZvcnQgdG8gZGV2ZWxvcCBhbiBpbnRlcmZhY2UgdG8gRm9ydHJhbiBzdGF0aXN0aWNhbCByb3V0aW5lcyB0byBhIGZ1bGwgZmVhdHVyZWQgbGFuZ3VhZ2UgdGhhdCBwcm92aWRlcyBhY2Nlc3MgdG8gdGhlIHdvcmxkJ3MgcmljaGVzdCByZXBvc2l0b3J5IG9mIHN0YXRpc3RpY2FsIGFuZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMuIFRoZSBmb2xsb3dpbmcgZGF0ZXMgb3V0bGluZSBhIHNob3J0IGhpc3Rvcnkgb2YgaG93IHRoaXMgaGFwcGVuZWQuCiAgICAgICAgICAgCiFbXShSaGlzdG9yeS5wbmcpCjxici8+ICAgICAgICAgCiAgICAgICAgICAKUiBpcyBhIGRpcmVjdCBkZXNjZW5kYW50IG9mIHRoZSBTIExhbmd1YWdlIGRldmVsb3BlZCBhdCBCZWxsIExhYnMgYnkgSm9obiBDaGFtYmVycywgUmljayBCZWNrZXIgYW5kIG90aGVyLCBidXQgaXQgd2FzIGFsc28gaGVhdmlseSBpbmZsdWVuY2VkIGJ5IExpc3AgYW5kIFNjaGVtZS4gUm9iZXJ0IEdlbnRsZW1hbiBhbmQgUm9zcyBJaGFrYSBpbnRyb2R1Y2VkIExpc3AgbGlrZSBzZW1hbnRpY3MgdG8gYW4gaWRlYXMgc3VjaCBhcyBsZXhpY2FsIHNjb3BpbmcuCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogCgojIyMgVGhlIFN0cnVjdHVyZSBvZiB0aGUgUiBMYW5ndWFnZQojIyMjUiBpcyBhbGwgYWJvdXQgIkZsb3ciCgpUaGUgUiBsYW5ndWFnZSB3YXMgZGVzaWduZWQgdG8gZW5oYW5jZSB0aGUgZXhwZXJpZW5jZSBvZiBkYXRhIGV4cGxvcmF0aW9uIGFuZCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UuIFRoZSBvcmlnaW5hbCBpbnRlbnQgb2YgdGhlIFMgZGVzaWduZXJzIHRvIG1ha2UgYSBjb252ZW5pZW50IGludGVyZmFjZSBmb3IgdGhlaXIgaGlnaCBwb3dlcmVkIEZvcnRyYW4gcm91cmluZXMgZXZvbHZlZCBpbnRvIGEgcHJvamVjdCB0byBtYWtlIFIgYSBmdWxsLWZlYXR1cmVkIGxhbmd1YWdlIGZvciBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgYW5kIG1vZGVsaW5nLiBQZW9wbGUgcHJvZHVjdGl2aXR5IGlzIGF0IHRoZSBjb3JlIG9mIHRoZSBSIGV4cGVyaWVuY2UuIEFzIEpvaG4gQ2hhbWJlcnMgcHV0cyBpdCBpbiBoaXMgYm9vayBbRXh0ZW5kaW5nIFJdKGh0dHBzOi8vd3d3LmFtYXpvbi5jb20vRXh0ZW5kaW5nLVItSm9obi1NLUNoYW1iZXJzL2RwLzExMzg0NjkyNzAvcmVmPXNyXzFfMT9pZT1VVEY4JnFpZD0xNTA2NzE3NDI1JnNyPTgtMSZrZXl3b3Jkcz1leHRlbmRpbmcrciksICoq4oCcT25lIG9mIHRoZSBhdHRyYWN0aW9ucyBvZiBSIGhhcyBhbHdheXMgYmVlbiB0aGUgYWJpbGl0eSB0byBjb21wdXRlIGFuIGludGVyZXN0aW5nIHJlc3VsdCBxdWlja2x5LiIqKiAKRWFzZSBvZiB1c2UgYW5kIHRoZSBhYmlsaXR5IHRvIHN0YXkgaW4gdGhlICJmbG93IiBvZiBhbiBhbmFseXNpcyBpcyBtb3JlIGltcG9ydGFudCB0aGFuIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeS4KCioqUiBpcyBhIGZ1bmN0aW9uYWwsIG9iamVjdC1iYXNlZCBsYW5ndWFnZS4qKiBUaGUgdGhyZWUgYmFzaWMgcHJpbmNpcGxlcyB1bmRlcmx5aW5nIFIgYXJlOiAgICAKKiAgRXZlcnl0aGluZyB0aGF0IGV4aXN0cyBpbiBSIGlzIGFuIG9iamVjdCAgICAKKiAgRXZlcnl0aGluZyB0aGF0IGhhcHBlbnMgaW4gUiBpcyBhIGZ1bmN0aW9uIGNhbGwgICAgCiogIEludGVyZmFjZXMgdG8gb3RoZXIgc29mdHdhcmUgYXJlIHBhcnQgb2YgUiAgICAKCkNoYW1iZXJzIGVsYWJvcmF0ZXMgb24gdGhpcyBsYXN0IHBvaW50IGFzIGZvbGxvd3M6ICoqIkEga2V5IG1vdGl2YXRpb24gZm9yIHRoZSBvcmlnaW5hbCBTIHJlbWFpbnMgaW1wb3J0YW50IG5vdzogdG8gZ2l2ZSBlYXN5IGFjY2VzcyB0byB0aGUgYmVzdCBjb21wdXRhdGlvbnMgZm9yIHVuZGVyc3RhbmRpbmcgZGF0YS4iKiogV2Ugc2VlIHNldmVyYWwgZXhhbXBsZXMgb2YgdGhlIGVmZm9ydHMgUiBkZXZlbG9wZXJzIHB1dCBpbnRvIGNvbm5lY3RpbmcgdG8gdGhlIGJlc3QgdGhpbmdzIG91dCB0aGVyZSBsYXRlciBpbiB0aGlzIHByZXNlbnRhdGlvbi4KCiMjIyMgT2JqZWN0cyBhbmQgRnVuY3Rpb25zClRoZSBpbnRlcnBsYXkgb2Ygb2JqZWN0cyBhbmQgZnVuY3Rpb25zIGluIFIgaXMgYXBwYXJlbnQgaW4gdGhlIHdheSBpbiB3aGljaCBkYXRhIGFuZCBzdGF0aXN0aWNhbCBtb2RlbHMgYXJlIHBhY2thZ2VkLiBGb3IgZXhhbXBsZSBjb25zaWRlciBhIHNpbXBsZSBsaW5lYXIgbW9kZWw6CgpgYGB7cn0KeCA8LSBybm9ybSgxMDApCnkgPC0gcm5vcm0oMTAwKQoKcmVnIDwtIGxtKHkgfiB4KQpzdW1tYXJ5KHJlZykKYGBgCgpBcyB0aGUgc3VtbWFyeSBhYm92ZSBpbmRpY2F0ZXMsIFIgb25seSByZXR1cm5zIG1vZGVsIG91dHB1dHMgd2hlbiB5b3UgYXNrIGZvciB0aGVtIGFuZCBtZXRob2RzIGZvciBnZW5lcmF0aW5nIHN1bW1hcmllcyB0ZW5kIHRvIGJlIHBhcnNpbW9uaW91cy4gVGhlIG1vZGVsIG9iamVjdCBgcmVnYCBwYWNrYWdlcyBxdWl0ZSBhIGJpdCBvZiBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbW9kZWwuCgpgYGB7cn0Kc3RyKHJlZyxnaXZlLmF0dHI9RkFMU0UpCgpgYGAKCgoqKkZ1bmN0aW9ucyBpbiBSIGNhbiBjYWxsIG90aGVyIGZ1bmN0aW9ucy4qKiBUaGlzIGZlYXR1cmUgaXMgd2VsbCBhZGFwdGVkIHRvIHdyaXRpbmcgZnVuY3Rpb25zIHRvIGVzdGltYXRlIG1heGltdW0gbGlrZWxpaG9vZCBhbmQgb3RoZXIgc3RhdGlzdGljYWwgYWxnb3JpdGhtcy4KCmBgYHtyfQojIFR3byBkaWZmZXJlbnQgcm91bmRpbmcgb3B0aW9ucwpyb3VuZChwaSw0KTsgc2lnbmlmKHBpLDQpICAgICAKIyBUaGlzIGlzIHRoZSB3YXkgdG8gaGF2ZSBvbmUgZnVuY3Rpb24gY2FsbCBhbm90aGVyIGZ1bmN0aW9uCmptZWFuIDwtIGZ1bmN0aW9uKHgsRlVOLC4uLil7CiAgICAgICAgICAgICAgIG0gPC0gRlVOKHN1bSh4KS9sZW5ndGgoeCksLi4uKQogICAgICAgICAgICAgICByZXR1cm4obSl9Cgp4IDwtIHJub3JtKDEwMCkKam1lYW4oeCxyb3VuZCw0KTsgam1lYW4oeCwgc2lnbmlmLDQpCgpgYGAKCiMjIyMgTWlzc2luZyBWYWx1ZXMKQSBzaWduaWZpY2FudCBhZHZhbnRhZ2UgdGhhdCBSIGhhcyBvdmVyIG90aGVyIHNjcmlwdGluZyBsYW5ndWFnZXMgaXMgdGhhdCBpdCBoYXMgYW4gaW5uYXRlIG1lY2hhbmlzbSBmb3IgZGVhbGluZyB3aXRoIG1pc3NpbmcgdmFsdWVzOiAqKk5BKiouCmBgYHtyfQp6IDwtIGMoMTozLCBOQSkKegppcy5uYSh6KQoKYGBgCgpJdCBpcyBlYXN5IHRvIGlkZW50aWZ5IGFuZCB3b3JrIHdpdGggbWlzc2luZyB2YWx1ZXMgaW4gUi4gICAgCgpgYGB7cn0KYSA8LSAxOjUKYiA8LSBybm9ybSg1KQpjIDwtIExFVFRFUlNbMTo1XQpkRiA8LSBkYXRhLmZyYW1lKGEsYixjKQpkRiRiWzNdIDwtIE5BOyBkRiRjWzRdIDwtIE5BCmRGCm5hLm9taXQoZEYpCmBgYAoKUiBhbHNvIGhhcyBzZXZlcmFsIHBhY2thZ2VzIGRldm90ZWQgdG8gbWlzc2luZyB2YWx1ZSBpbXB1dGF0aW9uIGluY2x1ZGluZzogICAgICBbQW1lbGlhXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPUFtZWxpYSksIFtCYUJvb05dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9QmFCb29OKSwgICAgW2NhdF0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1jYXQpLCBbRm9ySW1wXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPUZvckltcCksICAgICAgW0htaXNjXShodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPUhtaXNjKSwgICAgICBbaW1wdXRlXShodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvaW1wdXRlLmh0bWwpLCAgICAgIFtpbXB1dGVNRFJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9aW1wdXRlTURSKSwgW2ttaV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1rbWkpLCAgICBbbWldKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9bWkpLCBbbWljZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1taWNlKSwgICAgICAgW01JbWl4XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPU1JbWl4KSwgIFttaXNzRm9yZXN0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPW1pc3NGb3Jlc3QpLCAgW01pc3NpbmdEYXRhR3VpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPU1pc3NpbmdEYXRhR1VJKSwgICAgW21pc3NNREFdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9bWlzc01EQSksICAgICBbbWl0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1taXRvb2xzKSwgW21peF0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1taXgpLCAgICAgW210c2RpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPW10c2RpKSwgW25vcm1dKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9bm9ybSksICAgICBbcGFuXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXBhbiksICAgICAgW3JvYkNvbXBvc2l0aW9uc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1yb2JDb21wb3NpdGlvbnMpLCAgICAgW3JyY292TkFdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cnJjb3ZOQSksICAgICAgW3NiZ2NvcF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1zYmdjb3ApLCBbVklNXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPVZJTSksICAgICAgW3lhSW1wdXRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXlhSW1wdXRlKSwgW1plbGlnXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPVplbGlnKSAgIAoKIyMjIyBUaGUgRGF0YSBGcmFtZQpBIGRhdGEgZnJhbWUgaXMgYSBsaXN0IGluIHdoaWNoIGVhY2ggcm93IG1heSByZXByZXNlbnQgYW4gb2JzZXJ2YXRpb24gYW5kIGVhY2ggY29sdW1uIGEgdmFyaWFibGUsIGlzIGEgbmF0dXJhbCBkYXRhIHN0cnVjdHVyZSBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMuIFRoaXMgY29uc2lzdGVudCwgdWJpcXVpdG91cyBkYXRhIHN0cnVjdHVyZSBwcm92aWRlcyBSIHdpdGggYSBiaWcgdGVjaG5pY2FsIGFkdmFudGFnZS4gVGhpcyBpcyBubyBhY2NpZGVudC4gQm90aCBSb2JlcnQgR2VudGxlbWFuIGFuZCBSb3NzIEloYWthIGJlbGlldmVkIGluIFtOaWtsYXVzIFdpcnRo4oCZc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTmlrbGF1c19XaXJ0aCkgZGljdHVtOiBbYGFsZ29yaXRobXMgKyBkYXRhIHN0cnVjdHVyZXMgPSBwcm9ncmFtc2BdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0FsZ29yaXRobXNfJTJCX0RhdGFfU3RydWN0dXJlc18lM0RfUHJvZ3JhbXMpLgoKIyMgKl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qIAoKIyMjIFRoZSBSIFBhY2thZ2UgU3lzdGVtClRoZXJlIGFyZSBvdmVyIDExLDcwMCBwYWNrYWdlcyBvbiBDUkFOLCBSJ3MgY2VudHJhbCByZXBvc2l0b3J5LiBUaGUgYWxnb3JpdGhtcyBpbiB0aGVzZSBwYWNrYWdlcyBjb21wcmlzZSBhIHRyYW5zcGFyZW50LCBkb2N1bWVudGVkIHN0YXRpc3RpY2FsIHJlc291cmNlIG9mIGdyZWF0IHZhbHVlLiBTZWFyY2hpbmcgdGhlIHRocm91Z2ggQ1JBTiBpcyBhIGNoYWxsZW5nZSwgYnV0IHRoZSBbQ1JBTiBUYXNrIFZpZXdzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvdmlld3MvKSBsaXN0cyBvZiBSIHBhY2thZ2VzLCBjdXJhdGVkIGJ5IGV4cGVydHMgYW5kIG9yZ2FuaXplZCBieSBhcHBsaWNhdGlvbiBhcmVhIG1pdGlnYXRlIHRoZSBwcm9ibGVtLgoKIVtdKHRhc2t2aWV3cy5wbmcpCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogCgojIyBUb29scyBmb3IgV29ya2luZyBpbiBSCgpBIGdvb2QgcGxhY2UgdG8gc3RhcnQgaXMgd2l0aCBhbiBJREUgYW5kIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tLykKTGV0J3MgdGFrZSBhIGZldyBtaW51dGVzIHRvIGV4YW1pbmUgdGhpcyBub3RlYm9vayBhbmQgdGhlIFIgTWFya2Rvd24gY29kZS4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIFRvb2xzIGZvciBNYW5pcHVsYXRpbmcgRGF0YQojIyMgVGhlIFRpZHl2ZXJzZQpBbm90aGVyIGdyZWF0IGFkdmFudGFnZSBvZiBSIGlzIGl0cyBhYmlsaXR5IHRvIGZhY2lsaXRhdGUgdGhlIGNvbnN0cnVjdGlvbiBvZiBEb21haW4gU3BlY2lmaWMgTGFuZ3VhZ2VzLiBBcyBTaGlueSdzIGNyZWF0b3IgSm9lIENoZW5nIHB1dHMgaXQgWyhJbnRlcmlldyB3aXRoIEpvZSBDaGVuZyldKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTcvMDEvMDQvaW50ZXJ2aWV3LXdpdGgtam9lLWNoZW5nLyk6Cgo+IElmIHlvdSBsb29rIGJleW9uZCB0aGUgc3ludGF4LCBSIHJlYWxseSBpcyBjb25jZXB0dWFsbHkgdmVyeSBtdWNoIGxpa2UgTGlzcCBpbiBhIGxvdCBvZiB3YXlzLiBPbmUgb2YgdGhvc2Ugd2F5cyBpcyB0aGF0IGl0IG1ha2VzIGl0IHZlcnksIHZlcnkgZWFzeSB0byBjb21wdXRlIG9uIHRoZSBwcm9ncmFtbWluZyBsYW5ndWFnZSBpdHNlbGYuIC4uLgoKPiBJIHRoaW5rIGRheS10by1kYXksIFIgcHJvZ3JhbW1lcnMgcHJvYmFibHkgZG9u4oCZdCB0aGluayBhYm91dCB0aGVzZSB0aGluZ3MsIGJ1dCB0aGUgZWxlZ2FudCwgdGVyc2Ugc3ludGF4IG9mIGRwbHlyIGFuZCB0aGUgcGlwZSBvcGVyYXRvciBhcmUgcG9zc2libGUgYmVjYXVzZSBvZiBob3cgbWFsbGVhYmxlIGEgbGFuZ3VhZ2UgUiBpcyBhbmQgaG93IGdyZWF0IGl0IGlzIGZvciB3cml0aW5nIERTTHMgaW4gaXQuCgo+IFBlcnNvbmFsbHksIG9uZSBvZiBteSBwZXQgcGVldmVzIGR1cmluZyB0aGVzZSBsYW5ndWFnZSB3YXJzIGlzIHdoZW4gcGVvcGxlIHNheSB0aGF0IG9uZSBvZiB0aGUgZGllcmVuY2VzIGJldHdlZW4gc2F5IFB5dGhvbiBvciBKdWxpYSBhbmQgUiBpcyB0aGF0IFIgaXMgYSBEU0wgZm9yIHN0YXRzLCB3aGVyZWFzIHRoZXNlIG90aGVyIHRoaW5ncyBhcmUgZ2VuZXJhbCBwdXJwb3NlIGxhbmd1YWdlcy4gUiBpcyBub3QgYSBEU0wuIEl04oCZcyBhIGxhbmd1YWdlIGZvciB3cml0aW5nIERTTHMsIHdoaWNoIGlzIHNvbWV0aGluZyB0aGF04oCZcyBhbHRvZ2V0aGVyIG1vcmUgcG93ZXJmdWwuIEkgYWN0dWFsbHkgdGhpbmsgdGhhdCBKdWxpYSBoYXMgbWFueSBvZiB0aGVzZSBzYW1lIGNoYXJhY3RlcmlzdGljcywgYnV0IFB5dGhvbiwgZXZlbiB0aG91Z2ggaXQgb2J2aW91c2x5IGhhcyBpdHMgb3duIHN0cmVuZ3RocywgY2VydGFpbmx5IGRvZXNu4oCZdCBzaGFyZSB0aGF0IHNhbWUgbGV2ZWwgb2YgZmV4aWJpbGl0eS4KCk9uZSB3YXkgdG8gdGhpbmsgb2YgdGhlIFtUaWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSBpcyB0aGF0IGl0IGlzIGEgRFNMIGZvciBkb2luZyBkYXRhIHNjaWVuY2UuIFRoZSBwYWNrYWdlcyB0aGF0IGNvbXByaXNlIHRoZSB0aWR5dmVyc2Ugb2ZmZXIgYSBjb25zaXN0ZW50LCBpbnRlZ3JhdGVkIHNldCBvZiBmdW5jdGlvbnMgZm9yIGltcGxlbWVudGluZyB0aGUgY2Fub25pY2FsIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4KCiFbXSh0aWR5dmVyc2UucG5nKQo8YnIvPgo8YnIvPgpUaGUgbmFtZSAidGlkeXZlcnNlIiBjb21lcyBmcm9tIHRoZSBjb25jZXB0IG9mICJ0aWR5IiBkYXRhIHdoaWNoIEhhZGxleSBXaWNraGFtLCB0aGUgdGlkeXZyZXNlJ3MgcHJpbmNpcGFsIGFyY2hpdGVjdCBhbmQgaW1wbGVtZW50ZXIsIGRlc2NyaWJlcyBhcyBmb2xsb3dzOiAKCj4gW1RpZHkgZGF0YV0oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSBpcyBhIHN0YW5kYXJkIHdheSBvZiBtYXBwaW5nIHRoZSBtZWFuaW5nIG9mIGEgZGF0YXNldCB0byBpdHMgc3RydWN0dXJlLiBBIGRhdGFzZXQgaXMgbWVzc3kgb3IgdGlkeSBkZXBlbmRpbmcgb24gaG93IHJvd3MsIGNvbHVtbnMgYW5kIHRhYmxlcyBhcmUgbWF0Y2hlZCB1cCB3aXRoIG9ic2VydmF0aW9ucywgdmFyaWFibGVzIGFuZCB0eXBlcy4gSW4gdGlkeSBkYXRhOiAoMSkgRWFjaCB2YXJpYWJsZSBmb3JtcyBhIGNvbHVtbiwgKDIpIEVhY2ggb2JzZXJ2YXRpb24gZm9ybXMgYSByb3cgYW5kICgzKSBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUuCgojIyMjIE1hbmlwdWxhdGluZyBhbmQgVHJhbnNmb3JtaW5nIERhdGEKVGhlIGBkcGx5cmAgcGFja2FnZSB3aGljaCBwcm92aWRlcyBhIGNvbnNpc3RlbnQgc2V0IG9mIHZlcmJzIGZvciBtYW5pcHVsYXRpbmcgZGF0YSBwcm92aWRlcyB0aGUgYmFja2JvbmUgc3RydWN0dXJlIGZvciB0aGUgaW1wb3J0LCB0eWR5IGFuZCB0cmFuc2Zvcm0gc2VxdWVuY2Ugb2YgdGhlIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4gVGhlIGJhc2ljIGBkcGx5cmAgdmVyYnMgYXJlOgoqIGBmaWx0ZXIoKWAgdG8gc2VsZWN0IGNhc2VzIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyAgICAKKiBgYXJyYW5nZSgpYCB0byByZW9yZGVyIHRoZSBjYXNlcyAgIAoqIGBzZWxlY3QoKWAgYW5kIGByZW5hbWUoKWAgdG8gc2VsZWN0IHZhcmlhYmxlcyBiYXNlZCBvbiB0aGVpciBuYW1lcyAgIAoqIGBtdXRhdGUoKWAgYW5kIGB0cmFuc211dGUoKWAgdG8gYWRkIG5ldyB2YXJpYWJsZXMgdGhhdCBhcmUgZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcyAgICAKKiBgc3VtbWFyaXNlKClgICB0byBjb25kZW5zZSBtdWx0aXBsZSB2YWx1ZXMgdG8gYSBzaW5nbGUgdmFsdWUgICAgCiogYHNhbXBsZV9uKClgIGFuZCBgc2FtcGxlX2ZyYWMoKWAgIHRvIHRha2UgcmFuZG9tIHNhbXBsZXMKCkhlcmUsIHdlIHdpbGwgbG9vayBhdCBhIGZldyBleGFtcGxlcyBvZiB3b3JraW5nIHdpdGggdGhlc2UgdmVyYnMgdXNpbmcgbnljZmxpZ2h0czEzOjpmbGlnaHRzLCBhIGRhdGFzZXQgY29udGFpbmluZyBhbGwgMzM2Nzc2IGZsaWdodHMgdGhhdCBkZXBhcnRlZCBmcm9tIE5ldyBZb3JrIENpdHkgaW4gMjAxMy4gVGhlIGRhdGEgY29tZXMgZnJvbSB0aGUgW1VTIEJ1cmVhdSBvZiBUcmFuc3BvcnRhdGlvbiBTdGF0aXN0aWNzXShodHRwczovL3d3dy50cmFuc3RhdHMuYnRzLmdvdi9EYXRhYmFzZUluZm8uYXNwP0RCX0lEPTEyMCZMaW5rPTApLCBhbmQgaXMgZG9jdW1lbnRlZCBpbiA/bnljZmxpZ2h0czEzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIsIm55Y2ZsaWdodHMxMyIpCmxpYnJhcnkobnljZmxpZ2h0czEzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZGF0YShmbGlnaHRzKQpmbGlnaHRzCmBgYAoKCkluIHRoaXMgZmlyc3QgZXhhbXBsZSwgd2UgdXNlIGBkcGx5cjo6ZmlsdGVyKClgIHRvIGZldGNoIGZsaWdodHMgdGhhdCBhcnJpdmVkIGVhcmx5IGluIEphbnVhcnkuCgpgYGB7cn0KIyBmaWx0ZXIgYnkgZGVwYXJ0dXJlIGRlbGF5IGFuZCBwcmludCB0aGUgZmlyc3QgZmV3IHJlY29yZHMKZmxpZ2h0cyAlPiUgZmlsdGVyKG1vbnRoID09IDEsIGRlcF9kZWxheSA8IDApCmBgYAoKYGFycmFuZ2UoKWAgcmVvcmRlcnMgcm93cy4gSXQgdGFrZXMgYSBkYXRhIGZyYW1lLCBhbmQgYSBzZXQgb2YgY29sdW1uIG5hbWVzIChvciBtb3JlIGNvbXBsaWNhdGVkIGV4cHJlc3Npb25zKSB0byBvcmRlciBieS4gSWYgeW91IHByb3ZpZGUgbW9yZSB0aGFuIG9uZSBjb2x1bW4gbmFtZSwgZWFjaCBhZGRpdGlvbmFsIGNvbHVtbiB3aWxsIGJlIHVzZWQgdG8gYnJlYWsgdGllcyBpbiB0aGUgdmFsdWVzIG9mIHByZWNlZGluZyBjb2x1bW5zOgoKYGBge3J9CmFycmFuZ2UoZmxpZ2h0cywgc2NoZWRfZGVwX3RpbWUsIGRlcF9kZWxheSkKCmBgYAoKYHNlbGVjdCgpYCBhbGxvd3MgeW91IHRvIHNlbGVjdCBjb2x1bW5zIGJ5IG5hbWUuIEhlcmUgd2Ugc2VsZWN0IHRoZSBjb2x1bW5zIGBvcmlnaW5gLCBgZGVzdGAgYW5kIGBhaXJfdGltZWAsIGFuZCB0aGVuIHNvcnQgYnkgYGFpcl90aW1lYCBmcm9tIGxhcmdlc3QgdG8gc21hbGxlc3QuCgpgYGB7cn0Kc2VsZWN0KGZsaWdodHMsIG9yaWdpbiwgZGVzdCwgYWlyX3RpbWUpICU+JSBhcnJhbmdlKGRlc2MoYWlyX3RpbWUpKQpgYGAKCmBtdXRhdGUoKWAgYWxsb3dzIHlvdSB0byBhZGQgYSBuZXcgY29sdW1uIHRvIGEgZGF0YSBmcmFtZS4gSGVyZSB3ZSBhZGQgaW4gYSBjb2x1bW4gZm9yIGFpcnNwZWVkLgoKYGBge3J9Cm11dGF0ZShmbGlnaHRzLAogIGdhaW4gPSBhcnJfZGVsYXkgLSBkZXBfZGVsYXksCiAgc3BlZWQgPSBkaXN0YW5jZSAvIGFpcl90aW1lICogNjAKKQpgYGAKCmBzdW1tYXJpc2UoKWAgd2lsbCBjb21wdXRlIGEgc3RhdGlzdGljIGFuZCBjb2xsYXBzZSBhIGRhdGEgZnJhbWUgaW50byBhIHNpbmdsZSByb3cuCgpgYGB7cn0KZHBseXI6OnN1bW1hcmlzZShmbGlnaHRzLAogIGRlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkKKQpgYGAKClRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb25zIGFsbG93cyB5b3UgdXNlIHRoZSB2ZXJicyBkaXNjdXNzZWQgYWJvdmUgb24gZ3JvdXBzIG9mIG9ic2VydmF0aW9ucyBpbiBhIGRhdGFzZXQuIEhlcmUgd2UgY29tcHV0ZSBtZWFuIGRpc3RhbmNlIGFuZCBhcnJpdmFsIGRlbGF5IGZvciBlYWNoIGluZGl2aWR1YWwgcGxhbmUgYW5kIHBsb3QgdGhlIHJlc3VsdHMuCgpgYGB7cix3YXJuaW5nPUZBTFNFfQpieV90YWlsbnVtIDwtIGdyb3VwX2J5KGZsaWdodHMsIHRhaWxudW0pCmRlbGF5IDwtIGRwbHlyOjpzdW1tYXJpc2UoYnlfdGFpbG51bSwKICBjb3VudCA9IG4oKSwKICBkaXN0ID0gbWVhbihkaXN0YW5jZSwgbmEucm0gPSBUUlVFKSwKICBkZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpKQpkZWxheSA8LSBmaWx0ZXIoZGVsYXksIGNvdW50ID4gMjAsIGRpc3QgPCAyMDAwKQoKIyBwbG90IGRlbGF5cwpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChkZWxheSwgYWVzKGRpc3QsIGRlbGF5KSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjb3VudCksIGFscGhhID0gMS8yKSArCiAgZ2VvbV9zbW9vdGgoKSAgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDQpCmBgYAoKCgpGb3IgYSBtb3JlIGNvbXBsZXRlIHByZXNlbnRhdGlvbiB1c2luZyB0aGlzIGRhdGEgc2VlIHRoZSBbZHBseXIgdmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kcGx5ci92aWduZXR0ZXMvZHBseXIuaHRtbCkuCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogCgoKIyMgVG9vbHMgZm9yIEFjY2Vzc2luZyBEYXRhCiMjIyBBY2Nlc3NpbmcgRGF0YWJhc2VzCldpdGggdGhlIGFpZCBvZiB0aGUgYERCSWAgcGFja2FnZSwgYGRwbHlgIGlzIGFibGUgdG8gaW1wb3J0IGRhdGEgZnJvbSBzZXZlcmFsIGRpZmZlcmVudCBvcGVuIHNvdXJjZSBkYXRhIGJhc2VzIGluY2x1ZGluZyAqTXlTUUwqIGFuZCAqTWFyaWFEQiogd2l0aCB0aGUgW2BSTWFyaWFEQmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3RhdHMtZGIvUk1hcmlhREIpIHBhY2thZ2UsICpQb3N0Z3JlcyogYW5kICpSZWRzaGlmdCogd2l0aCBbYFJQb3N0Z3Jlc3NTUUxgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUlBvc3RncmVTUUwvKSwgYW5kICpTUUxpdGUqIHdpdGggW2BSU1FMaXRlYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1JTUUxpdGUvaW5kZXguaHRtbCkuIAoKVGhlIGZvbGxvd2luZyBzaW1wbGUgZXhhbXBsZSBpbGx1c3RyYXRlcyB3b3JraW5nIHdpdGggU1FMaXRlLiBBZ2Fpbiwgd2UgdXNlIHRoZSBueWNmbGlnaHRzMTMgZGF0YSBzZXQgdG8gY3JlYXRlIGEgU1FMaXRlIHRhYmxlIHdpdGggMSwwMDAgcm93cyBhbmQgMTkgY29sdW1ucy4KYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KERCSSkKbGlicmFyeShSU1FMaXRlKQpsaWJyYXJ5KGRicGx5cikKbGlicmFyeShueWNmbGlnaHRzMTMpCgpjb24gPC0gREJJOjpkYkNvbm5lY3QoUlNRTGl0ZTo6U1FMaXRlKCksIHBhdGggPSAiOm1lbW9yeToiKQoKY29weV90byhjb24sIG55Y2ZsaWdodHMxMzo6ZmxpZ2h0cywgImZsaWdodHMiLAogIHRlbXBvcmFyeSA9IEZBTFNFLCAKICBpbmRleGVzID0gbGlzdCgKICAgIGMoInllYXIiLCAibW9udGgiLCAiZGF5IiksIAogICAgImNhcnJpZXIiLCAKICAgICJ0YWlsbnVtIiwKICAgICJkZXN0IgogICkKKQoKZmxpZ2h0c19kYiA8LSB0YmwoY29uLCAiZmxpZ2h0cyIpCmZsaWdodHNfZGIgCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBlc3RhYmxpc2hlZCBhIGNvbm5lY3Rpb24gdG8gdGhlIGRhdGFiYXNlIHRhYmxlLCB3ZSBjYW4gdXNlIHRoZSBub3JtYWwgZHBseXIgdmVyYnMgdG8gcXVlcnkgdGhlIGRhdGEuCgpgYGB7cn0KIyBMaXN0IGRlcGFydHVyZSBkZWxheSBhbmQgYXJyaXZhbCBkZWxheSBmb3IgeWVhciBtb250aCBhbmQgZGF5LgpmbGlnaHRzX2RiICU+JSBzZWxlY3QoeWVhcjpkYXksIGRlcF9kZWxheSwgYXJyX2RlbGF5KQoKIyBGaW5kIHJlY29yZHMgd2l0aCBkZXBhcnR1cmUgZGVsYXkgZ3JlYXRlciB0aGFuIDI0MCBtaW51dGVzLgpmbGlnaHRzX2RiICU+JSBmaWx0ZXIoZGVwX2RlbGF5ID4gMjQwKQoKIyBTdW1tYXJpemUgbWVhbiBkZXBhcnR1cmUgZGVsYXkgYnkgZGVzdGluYXRpb24gYWlycG9ydApmbGlnaHRzX2RiICU+JSAKICBncm91cF9ieShkZXN0KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGRlbGF5ID0gbWVhbihkZXBfZGVsYXkpKQoKIyBkaXNjb25uZWN0IGZyb20gZGF0YWJhc2UKZGJEaXNjb25uZWN0KGNvbikKYGBgCgpgZHBseXJgIGNhbiBhbHNvIGJlIHVzZSB3aXRoIGRhdGEgc3RvcmVkIGluIGNvbW1lcmNpYWwgZGF0YWJhc2VzLiBUaGUgW2BiaWdxdWVyeWBdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9YmlncnF1ZXJ5KSBlbmFibGVzIHdvcmtpbmcgd2l0aCBkYXRhIGluIEdvb2dsZSdzIFtCaWdRdWVyeV0oaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2JpZ3F1ZXJ5LykgcGxhdGZvcm0sIGFuZCB0aGUgW2BvZGJjYF0ocGFja2FnZSkgcGVybWl0cyBtYW55IGNvbW1lcmNpYWwgZGF0YWJhc2VzIHRvIGJlIHVzZWQgYXMgYSBgZHBseXJgIGJhY2tlbmQgdGhyb3VnaCB0aGUgb3BlbiBkYXRhYmFzZSBjb25uZWN0aXZpdHkgcHJvdG9jb2wsCgpGb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiBiYXNpYyBgZGJwbHlyYCBmdW5jdGlvbmFsaXR5IHNlZSB0aGUgW3ZpZ25ldHRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZGJwbHlyL3ZpZ25ldHRlcy9kYnBseXIuaHRtbCkuIEZvciBhbiBhZHZhbmNlZCB0cmVhdG1lbnQgdXNpbmcgYGRwbHlyYCB3aXRoIGNvbW1lcmNpYWwgZ3JhZGUgZGF0YWJhc2VzIHNlZSB0aGUgdGhyZWUgYmxvZyBwb3N0cyBmcm9tIEVkZ2FyIFJ1aXo6ICAgIAoqIFtEYXRhYmFzZXMgdXNpbmcgUl0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wNS8xNy9kYXRhYmFzZXMtdXNpbmctci8pICAgCiogW1Zpc3VhbGl6YXRpb25zIHdpdGggUiBhbmQgRGF0YWJhc2VzXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzA4LzE2L3Zpc3VhbGl6YXRpb25zLXdpdGgtci1hbmQtZGF0YWJhc2VzLykgCiogW0RhdGFiYXNlIFF1ZXJpZXMgd2l0aCBSXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzE4L2RhdGFiYXNlLXF1ZXJpZXMtd2l0aC1yLykKKiBbRW50ZXJwcmlzZS1yZWFkeSBkYXNoYm9hcmRzIHdpdGggU2hpbnkgYW5kIGRhdGFiYXNlc10oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wOS8yMC9kYXNoYm9hcmRzLXdpdGgtci1hbmQtZGF0YWJhc2VzLykuCgoqKkJlIHN1cmUgdG8gY2F0Y2ggRWRnYXIncyB0YWxrICJEYXRhYmFzZXMgVXNpbmcgUiIgMTE6MzAgQU0gdG9tb3Jyb3cgaW4gcm9vbSBUUjIqKgoKIyMgKl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qIAoKIyMgVG9vbHMgZm9yIFZpc3VhbGl6aW5nIERhdGEKIyMjIEphdmFzY3JpcHQgVmlzdWFsaXphdGlvbnMgd2l0aCBodG1sd2lkZ2V0cwoKVGhlcmUgYXJlIHRocmVlIG1ham9yIHBsb3R0aW5nIHN5c3RlbXMgaW4gUjogKDEpIGJhc2UgZ3JhcGhpY3MsICgyKSBsYXR0aWNlIGdyYXBoaWNzIGFuZCAoMykgZ2dwbG90MiwgYW4gZXhhbXBsZSBvZiB3aGljaCB3ZSBoYXZlIHNlZW4gYWJvdmUuIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBhbW91bnQgb2YgZGV2ZWxvcG1lbnQgd29yayBnb2luZyBvbiB0byBhbGxvdyBSIHVzZXJzIHRvIHByb2R1Y2UgSmF2YVNjcmlwdCB2aXN1YWxpemF0aW9ucyBkaXJlY3RseSBmcm9tIFIuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHNlZSBhbiBleGFtcGxlIG9mIHdvcmtpbmcgd2l0aCBSJ3MgW2h0bWx3aWRnZXRzXShodHRwOi8vd3d3Lmh0bWx3aWRnZXRzLm9yZy8pIGxpYnJhcnkuCgpGb3IgdGhpcyBleGFtcGxlIHdlIHdpbGwgd29yayB3aXRoIHRoZSBodG1sd2lkZ2V0IGBkeWdyYXBoc2Agd2hpY2ggaXMgdXNlZnVsIGZvciBwcm9kdWNpbmcgaW50ZXJhY3RpdmUgdGltZSBzZXJpZXMgdmlzdWFsaXphdGlvbnMgYW5kIHVzZSBpdCB0byB2aXN1YWxpemUgdGhlIG1vbnRobHkgZmxvdyBmcm9tIHRoZSBOaWxlIFJpdmVyIGZyb20gMTg3MSB0aHJvdWdoIDE5ODQuCgpgYGB7cn0gIApsaWJyYXJ5KHByYWNtYSkgICAgICAgICAgICAgIyBmb3IgTmlsZSByaXZlciBkYXRhCmRhdGEobmlsZSkKaGVhZChuaWxlKQpgYGAKClRoaXMgZGF0YSBzZXQgaXMgbm90IHRpZHkuIEZvciBlYWNoIHllYXIsIHRoZSBpbmZvcm1hdGlvbiBhYm91dCBtb250aGx5IGZsb3cgaXMgc3ByZWFkIG92ZXIgc2V2ZXJhbCBjb2x1bW5zLiBUbyB0aWR5IHRoaW5ncyB1cCwgd2UgbmVlZCB0byByZXNoYXBlIHRoZSBkYXRhIGZyYW1lIGludG8gImxvbmcgZm9ybSIgd2hlcmUgdGhlIGNvbHVtbnMgY29tcHJpc2UgdGhlIHZhcmlhYmxlcyBZZWFyLCBNb250aCBhbmQgRmxvdy4gSW4gdGhlIGNvZGUgYmVsb3csIHRoZSBgdGlkeWAgZnVuY3Rpb24gYGdhdGhlcigpYCBhY2NvbXBsaXNoZXMgdGhpcy4gYGFycmFuZ2UoKWAgc29ydHMgdGhlIGRhdGEgYnkgWWVhciBhbmQgTW9udGgsIGFuZCBtdXRhdGUgYWRkcyBhIG5ldyBkYXRlIHZhcmlhYmxlIHRvIHRoZSBkYXRhIGZyYW1lLgoKYGBge3J9Cm5pbGVfbG9uZyA8LSBuaWxlICU+JSAKICAgICAgICAgICAgICAgICAgZ2F0aGVyKEphbjpEZWMsIGtleSA9ICJNb250aCIsIHZhbHVlID0gIkZsb3ciKSAlPiUgCiAgICAgICAgICAgICAgICAgIGFycmFuZ2UoWWVhcixtYXRjaChNb250aCxtb250aC5hYmIpKSAlPiUgCiAgICAgICAgICAgICAgICAgIGRwbHlyOjptdXRhdGUoCiAgICAgICAgICAgICAgICAgIERhdGUgPSBhcy5EYXRlKHBhc3RlKE1vbnRoLCItIiwiMTUiLCItIixhcy5jaGFyYWN0ZXIoWWVhciksc2VwPSIiKSwgZm9ybWF0PSIlYi0lZC0lWSIpKQogIApoZWFkKG5pbGVfbG9uZykKYGBgCgpOZXh0LCB3ZSBwcm9kdWNlIHNvbWUgZXhwbG9yYXRvcnkgcGxvdHMgd2l0aCBnZ3Bsb3QyCgpgYGB7cn0KIyBQbG90IHRoZSB0aW1lIHNlcmllcwpwIDwtIGdncGxvdChuaWxlX2xvbmdbMTAwOjMwMCxdLGFlcyh4PURhdGUseT1GbG93KSkKcCArIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludChzaGFwZT0xLGNvbD0icmVkIikgKyAKICB5bGFiKCJGbG93IGluIGN1YmljIG1ldGVycyAvIHNlY29uZCIpICsgCiAgZ2d0aXRsZSgiTW9udGhseSBGbG93IG9mIE5pbGUgUml2ZXIgYXQgRG9uZ29sYSBTdGF0aW9uIikKCgojIEJveHBsb3RzIG9mIG1vbnRobHkgZmxvd3MKYiA8LSBnZ3Bsb3QobmlsZV9sb25nLGFlcyhmYWN0b3IoTW9udGgpLEZsb3cpKQpiICsgZ2VvbV9ib3hwbG90KCkgKwogIHhsYWIoIk1vbnRoIikgKwogIHlsYWIoIkZsb3cgaW4gY3ViaWMgbWV0ZXJzIC8gc2Vjb25kIikgKyAKICBnZ3RpdGxlKCJWYXJpYXRpb24gb2YgRmxvdyBhdCBEb25nb2xhIFN0YXRpb24gYnkgTW9udGgiKQpgYGAgICAKCldlIGZpbmlzaCB1cCBieSBjcmVhdGluZyBhbiBpbnRlcmFjdGl2ZSwgSmF2YVNjcmlwdCBEMyBpbnRlcmFjdGl2ZSBwbG90LiBUbyBnZXQgdGhlIGRhdGEgcmVhZHkgZm9yIHRoZSBgZHlncmFwaCgpYCBmdW5jdGlvbiB3ZSBtdXN0IG1ha2UgaXQgYW4gYHh0c2AgdGltZSBzZXJpZXMgb2JqZWN0LgoKYGBge3J9CiMgQ3JlYXRlIGFuIGludGVyYWN0aXZlIGdyYXBoIHdpdGggYSBKYXZhc2NyaXB0IGxpYnJhcnkKbGlicmFyeSh4dHMpCmxpYnJhcnkoZHlncmFwaHMpCiMgTWFrZSBpbnRvIGEgdGltZSBzZXJpZXMgb2JqZWN0Cm5pbGVfdHMgPC0geHRzKG5pbGVfbG9uZyRGbG93LAogICAgICAgICAgICAgICBvcmRlci5ieT1uaWxlX2xvbmckRGF0ZSwKICAgICAgICAgICAgICAgZnJlcXVlbmN5PTEyLHN0YXJ0PWMoMTg3MSwxKSkKCiMgUGxvdCB3aXQgaHRtbHdpZGdldCBkeWdyYXBoCmR5Z3JhcGgobmlsZV90cyx5bGFiPSJjdWJpYyBtIC8gcyIsIAogICAgICAgIG1haW49Ik5pbGUgTW9udGhseSBGbG93IERhdGEiKSAlPiUKICBkeVNlcmllcygiVjEiLGxhYmVsPSJGbG93IikgJT4lCiAgZHlSYW5nZVNlbGVjdG9yKGRhdGVXaW5kb3cgPSBjKCIxODcxLTAxLTAxIiwiMTk4NC0xMi0wMSIpKQpgYGAgICAKClRvIHNlZSBzb21lIHNwZWN0YWN1bGFyIHZpc3VhbGl6YXRpb25zIGhhdmUgYSBsb29rIGF0IHRoZSBbaHRtbHdpZGdldHMgZm9yIFIgZ2FsbGVyeV0oaHR0cDovL2dhbGxlcnkuaHRtbHdpZGdldHMub3JnLykuCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogICAKCiMjIyBWaXN1YWxpemluZyBMYXJnZSBEYXRhIFNldHMgd2l0aCBUcmVsbGlzY29wZQpbVHJlbGxpc2NvcGVdKGh0dHBzOi8vaGFmZW4uZ2l0aHViLmlvL3RyZWxsaXNjb3BlanMvI3RyZWxsaXNjb3BlKSBpcyBhIHN5c3RlbSBmb3IgcHJvZHVjaW5nIGRldGFpbGVkIHZpc3VhbGl6YXRpb25zIG9mIHZlcnkgbGFyZ2UsIGNvbXBsZXggZGF0YXNldHMuIFRob3VzYW5kcyBvZiBwbG90cyBhcmUgcHJvZHVjZWQgYW5kIHRoZSBzeXN0ZW0gY29tcHV0ZXMgbWV0cmljcyBvbiBlYWNoIHBsb3QgdGhhdCBkZXNjcmliZSBpbnRlcmVzdGluZyBxdWFudGl0YXRpdmUgYW5kIHF1YWxpdGF0aXZlIGZlYXR1cmVzLiBVc2VycyBjYW4gdGhlbiBzYW1wbGUsIGZpbHRlciwgb3Igc29ydCB0aGUgcGxvdHMgYmFzZWQgb24gdGhlIG1ldHJpY3MuIEluIFRyZWxsaXNjb3BlIHRlcm1pbm9sb2d5LCB0aGVzZSBtZXRyaWNzIGFyZSBjYWxsZWQgImNvZ25vc3RpY3MiLCBhIHRlcm0gY29pbmVkIGJ5IEpvaG4gVHVrZXkgd2hpY2ggc3RhbmRzIGZvciDigJxjb21wdXRlciBndWlkaW5nIGRpYWdub3N0aWNz4oCdCgpbRXhhbXBsZSB1c2luZyBnYXBtaW5kZXIgbGlmZSBleHBlY3RhbmN5IGRhdGFdKGh0dHA6Ly9oYWZlbi5naXRodWIuaW8vdHJlbGxpc2NvcGVqcy1kZW1vL2dhcG1pbmRlcl9wbG90bHkvKQoKIVtdKHRyZWxsaXNjb3BlLnBuZykKPGJyLz4KClRyZWxsaXNjb3BlIGlzIHBhcnQgb2YgdGhlIFtEZWx0YVJob10oaHR0cDovL2RlbHRhcmhvLm9yZy8pIFByb2plY3QuIExvb2sgW2hlcmVdKGh0dHA6Ly9tbC5zdGF0LnB1cmR1ZS5lZHUvZG9jcy90cmVsbGlzY29wZS5sZGF2LjIwMTMucGRmKSBmb3IgYSBwYXBlciB0aGF0IHN1bW1hcml6ZXMgdGhlIHN5c3RlbS4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKgoKIyMgUHJlZGljdGl2ZSBNb2RlbGluZyBUb29scwoKU2VlIHRoZSBzZXBhcmF0ZSBjYXJldCBub3RlYm9vay4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAgCgoKIyMgVG9vbHMgZm9yIEJpZyBEYXRhIFBsYXRmb3JtcwpTZWUgdGhlIG5vdGVib29rczoKKiBSX0tlcmFzX1RlbnNvckZsb3cKKiBzcGFybGt5cgoKIyMgKl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qIAoKIyMgVG9vbHMgZm9yIFNoYXJpbmcgUmVzdWx0cwojIyMgU2hpbnkKClNoaW55IGlzIGFuIG9wZW4gc291cmNlIFIgcGFja2FnZSB0aGF0IHByb3ZpZGVzIGFuIGVsZWdhbnQgYW5kIHBvd2VyZnVsIHdlYiBmcmFtZXdvcmsgZm9yIGJ1aWxkaW5nIHdlYiBhcHBsaWNhdGlvbnMgdXNpbmcgUi4gU2hpbnkgaGVscHMgeW91IHR1cm4geW91ciBhbmFseXNlcyBpbnRvIGludGVyYWN0aXZlIHdlYiBhcHBsaWNhdGlvbnMgd2l0aG91dCByZXF1aXJpbmcgSFRNTCwgQ1NTLCBvciBKYXZhU2NyaXB0IGtub3dsZWRnZS4gU2hpbnkgaGFzIGJlY29tZSBhbiBlc3NlbnRpYWwgY29tbXVuaWNhdGlvbiBwbGF0Zm9ybSBmb3Igc2V2ZXJhbCBlbnRlcnByaXNlIGRhdGEgc2NpZW5jZSBjb21wYW5pZXMuCgpTb21lIEVudGVycHJpc2UgTGV2ZWwgU2hpbnkgQXBwbGljYXRpb25zOiAgICAKKiBbTFJUIFNpZ25hbCBBbmFseXNpcyBmb3IgYSBEcnVnXShodHRwczovL29wZW5mZGEuc2hpbnlhcHBzLmlvL0xSVGVzdC8pIGZyb20gdGhlIEZEQQoKIVtdKGxydC5wbmcpCgoqIFtUaGUgU2VhQ2xhc3MgUiBQYWNrYWdlXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzIzL3RoZS1zZWFjbGFzcy1yLXBhY2thZ2UvKSBmcm9tIFNlYWdhdGUgVGVjaG5vbG9naWVzXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzIzL3RoZS1zZWFjbGFzcy1yLXBhY2thZ2UvKQo8YnIvPgohW10oc2VhY2xhc3MucG5nKQoKPGJyLz4KClNvbWUgIkhvdyB0byIgU2hpbnkgcG9zdHM6CiogW0VudGVycHJpc2UtcmVhZHkgZGFzaGJvYXJkcyB3aXRoIFNoaW55IGFuZCBkYXRhYmFzZXNdKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTcvMDkvMjAvZGFzaGJvYXJkcy13aXRoLXItYW5kLWRhdGFiYXNlcy8pCiogW1BvcnRmb2xpbyBWb2xhdGlsaXR5IFNoaW55IEFwcF0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wOC8wOS9wb3J0Zm9saW8tdm9sYXRpbGl0eS1zaGlueS1hcHAvKQoqIFtQcmludGluZyBGcm9tIEZsZXggRGFzaGJvYXJkXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzA2LzI4L3ByaW50aW5nLWZyb20tZmxleC1kYXNoYm9hcmQvKQoKVG8gZ2V0IHN0YXJ0ZWQgd2l0aCBTaGlueToKKiBbVGhlIFNoaW55IFNob3djYXNlXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9zaGlueS9zaGlueS11c2VyLXNob3djYXNlLykKKiBbTGVhcm4gU2hpbnldKGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pCiogW1NoaW55IEdhbGxlcnldKGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykKCgoK